import {WsEventCallback} from "@/websocket/ws"
import Message from "@/websocket/message";
import {command} from "@/websocket/ws_const";
import * as types from "@/common/vuex.const";
import im from "@/api/im";
import store from "@/store";
import {router} from "@/router";
//缓存业务处理器类型
const handlerMap = new Map();
//心跳间隔时间
const heartbeatInterval = 25000;
//重连间隔时间
const retryConnInterValMs = 5000;
//心跳定时器id
let heartbeatIntervalId = null;
//连接重试定时器Id
let retryConnTimeoutId = null;

/**
 * @Description: 自定义ws事件回调处理
 * @Author: miaoguoxin
 * @Date: 2021/3/31
 */
export class WsBizCallback extends WsEventCallback {
    constructor() {
        super();
    }

    onRead(res, ws) {
        let message = new Message();
        message.decode(res.data);
        let handler = handlerFactory(message.commandType);
        handler.read(message, ws)
    }

    onOpen(res, ws) {
        console.log("连接已打开");
        //发送登录请求
        im.login();
        ws.needRetry = true;
    }

    onConnComplete(res, ws) {
        if (res.errMsg == 'connectSocket:ok') {
            console.log('成功连接服务器')
        } else {
            uni.showModal({
                title: '链接服务器失败'
            })
        }
    }

    onClose(res, ws) {
        console.log('WebSocket 已关闭！' + res);
        //先清除所有定时器
        clearTimers();
        console.log("当前重连标记:", ws.needRetry);
        if (ws.needRetry) {
            this.retryConn(ws)
        }
    }

    onConnError(res, ws) {
        console.log('WebSocket连接打开失败，请检查！' + res);
    }

    //重试连接
    retryConn(ws) {
        retryConnTimeoutId = setTimeout(function () {
            console.log("开始重连服务器");
            ws.connServer();
        }, retryConnInterValMs);
    }
}

class BaseMessageHandler {

    constructor(protoName) {
        this.protoType = im.getProto(protoName);
    }

    doRead(message, protoModel) {
        throw new Error("需要子类实现doRead")
    }

    _getBody(bodyArrBuf, type) {
        return this.protoType.decode(bodyArrBuf);
    }

    //入口方法，处理消息读取
    read(message, ws) {
        let protoModel = this._getBody(message.body, message.messageType);
        this.doRead(message, protoModel, ws);
    }

}

/**
 * @Description: 消息序号响应处理
 * @Author: miaoguoxin
 * @Date: 2021/5/12
 */
class MsgIdResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        console.log("收到消息序号响应:", protoModel.msgId, "当前时间：",  Date.now());
        uni.$emit('message_handle_' + command.GENERATE_ID.key,
            {message: message, model: protoModel, ws: ws});
    }
}

/**
 * 单聊接收消息处理(接收方)
 * @author miaoguoxin
 * @date 2021/3/31
 */
class P2PReceiveHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        uni.$emit('message_handle_' + command.P2P_NOTIFY.key,
            {message: message, model: protoModel, ws: ws});
    }
}


/**
 * 单聊消息响应处理器（发送方）
 * @author miaoguoxin
 * @date 2021/5/13
 */
class P2PResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        uni.$emit('message_handle_' + command.P2P_SENDER.key,
            {message: message, model: protoModel, ws: ws});
    }
}

/**
 * 单聊抵达对方后的ack响应处理
 * @author miaoguoxin
 * @date 2021/5/13
 */
class P2PNotifyAckResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        uni.$emit('message_handle_' + command.P2P_NOTIFY_TO_SENDER_ACK.key,
            {message: message, model: protoModel, ws: ws});
    }
}

/**
 * 鉴权响应处理
 * @author miaoguoxin
 * @date 2021/3/31
 */
class AuthResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        console.log("接收到鉴权响应" + new Date().getTime());
        uni.$emit('message_handle_' + command.AUTH.key,
            {message: message, model: protoModel, ws: ws});
        //鉴权成功后开始发送心跳
        heartbeatIntervalId = setInterval(() => {
            im.sendHeartbeat();
        }, heartbeatInterval)
    }
}

/**
 * 离线 消息拉取
 * @author miaoguoxin
 * @date 2021/4/15
 */
class OfflineMessagePullResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        uni.$emit('message_handle_' + command.OFFLINE_MSG_PULL.key,
            {message: message, model: protoModel, ws: ws})
    }
}

/**
 * @Description: 踢出登录用户
 * @Author: miaoguoxin
 * @Date: 2021/4/23
 */
class KickOutResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        console.log("接收到被踢出命令");
        let queryData = {
            sourceDeviceNumber: protoModel.sourceDeviceNumber,
            sourceIpAddr: protoModel.sourceIpAddr,
            userId: protoModel.userId,
            targetDeviceNumber: protoModel.targetDeviceNumber
        };
        //跳转登录页面，并退出web session
        clearLogin(ws, queryData);
    }
}

/**
 * @Description: 错误异常处理
 * @Author: miaoguoxin
 * @Date: 2021/4/23
 */
class ErrorResponseHandler extends BaseMessageHandler {
    constructor(protoName) {
        super(protoName);
    }

    doRead(message, protoModel, ws) {
        console.log("ErrorResponseHandler:", JSON.stringify(protoModel));
        uni.showToast({
            title: protoModel.message,
            icon: 'none',
            duration: 2000
        });
        if (protoModel.code == 401) {
            setTimeout(function () {
                clearLogin(ws);
            }, 1000);
        }
    }
}

const clearLogin = (ws, data) => {
    ws.closeConnect();
    router.replace({name: 'login', params: {data}});
    store.commit(types.USER_STATE_PREFIX + types.CLEAR_LOGIN_INFO);
};

const clearTimer = (id) => {
    if (id != null) {
        clearInterval(heartbeatIntervalId);
    }
    return null;
};

const clearTimers = () => {
    clearTimer(heartbeatIntervalId);
    heartbeatIntervalId = null;
    clearTimeout(retryConnTimeoutId);
    retryConnTimeoutId = null;
};


/**
 * @Description: 获取请求处理类
 * @Author: miaoguoxin
 * @Date: 2021/3/31
 */
const handlerFactory = (commandType) => {
    let handler = handlerMap.get(commandType);
    if (typeof handler == "undefined") {
        throw new Error("找不到处理类");
    }
    return handler;
};

handlerMap.set(command.GENERATE_ID.key, new MsgIdResponseHandler("im.MsgIdResponse"));
handlerMap.set(command.P2P_SENDER.key, new P2PResponseHandler("im.P2PResponse"));
handlerMap.set(command.P2P_NOTIFY.key, new P2PReceiveHandler("im.P2PRequest"));
handlerMap.set(command.P2P_NOTIFY_TO_SENDER_ACK.key, new P2PNotifyAckResponseHandler("im.P2PRequest"));
handlerMap.set(command.AUTH.key, new AuthResponseHandler("im.AuthResponse"));
handlerMap.set(command.OFFLINE_MSG_PULL.key, new OfflineMessagePullResponseHandler("im.OfflineMsgResponse"));
handlerMap.set(command.KICK_OUT.key, new KickOutResponseHandler("im.KickOutRequest"));
handlerMap.set(command.ERROR.key, new ErrorResponseHandler("im.ErrorResponse"));


